﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#ifndef _UXS_VARIANT_H
#define _UXS_VARIANT_H

#pragma once

#include "../../stl_core/ux_stl_core.h"

namespace UX_STL
{
namespace UXTypesExt
{
	using namespace UXTypes;



	//
	// Variant
	//

	struct TVariant
	{
	// types
	private:
		struct IValue
		{
		private:
			TypeId_t const	_id;
			
		public:
			IValue (TypeId_t id): _id(id) {}
			virtual ~IValue () {}
			TypeId_t				Id ()		const	{ return _id; }
			virtual IValue *		Copy ()		const = 0;
			virtual const void *	Ptr ()		const = 0;
			virtual void *			Ptr () = 0;
			virtual usize			SizeOf ()	const = 0;
		};


		template <typename T>
		struct TValue : IValue
		{
		private:
			T		_value;
	
		public:
			TValue () : _value(), IValue( TypeId<T>() ) {}
			TValue (const T& v) : _value(v), IValue( TypeId<T>() ) {}

			IValue *		Copy ()		const	override	{ return new TValue<T>( _value ); }
			const void *	Ptr ()		const	override	{ return &_value; }
			void *			Ptr ()	 			override	{ return &_value; }
			usize			SizeOf ()	const	override	{ return sizeof(_value); }
		};


		struct TPODValue : IValue
		{
		private:
			AutoPtr<uint8>	_data;
			usize			_size;

		public:
			TPODValue () : _data(null), _size(0), IValue(TypeId_t()) {}
			TPODValue (usize size, TypeId_t id) : _data(null), _size(size), IValue(id)
			{
				if ( _size != 0 )
				{
					_data = new uint8[size];
					ZeroMem( _data.ptr(), _size );
				}
			}
			
			TPODValue (const TPODValue &v) : _data(null), _size(v._size), IValue(TypeId_t())
			{
				if ( v._data != null )
				{
					_data = new uint8[_size];
					MemCopy(_data.ptr(), v._data.ptr(), _size);
				}
			}
			
			IValue *		Copy ()		const	override	{ return new TPODValue( *this ); }
			const void *	Ptr ()		const	override	{ return _data.ptr(); }
			void *			Ptr ()				override	{ return _data.ptr(); }
			usize			SizeOf ()	const	override	{ return _size; }
		};


	// variables
	private:
		AutoPtr< IValue >	_pValue;


	// methods
	private:
		template <typename T>
		void _Set(const T& value)
		{
			_pValue = new TValue<T>( value );
		}

	public:
		TVariant ()
		{}

		TVariant (const TVariant &sVar)
		{
			if ( sVar.IsDefined() )
				_pValue = sVar._pValue->Copy();
		}


		template <typename T>
		TVariant (const T& value)
		{
			_Set( value );
		}


		template <typename T>
		TVariant & operator = (const T& value)
		{
			_Set( value );
			return *this;
		}


		void CreatePOD (usize size, TypeId_t id = TypeId_t())
		{
			_pValue = new TPODValue( size, id );
		}


		template <typename T>
		const T & Get () const
		{
			if ( TypeId<T>() == _pValue->Id() )
				return *(const T *)_pValue->Ptr();
			
			WARNING( "incorrect requirement type" );
			return *(const T *) null;
		}

		template <typename T>
		T & Get ()
		{
			if ( TypeId<T>() == _pValue->Id() )
				return *(T *)_pValue->Ptr();
			
			WARNING( "incorrect requirement type" );
			return *(T *) null;
		}


		void * GetPtr ()
		{
			return IsDefined() ? _pValue->Ptr() : null;
		}


		void const * GetPtr () const
		{
			return IsDefined() ? _pValue->Ptr() : null;
		}


		usize GetSizeOf () const
		{
			return IsDefined() ? _pValue->SizeOf() : 0;
		}


		template <typename T>
		void CopyTo (T &value) const
		{
			value = Get<T>();
		}


		TypeId_t GetValueTypeId () const
		{
			return IsDefined() ? _pValue->Id() : TypeId_t();
		}

		bool IsDefined () const
		{
			return _pValue != null;
		}
	};

	typedef array< TVariant, TCopyStrategy<TVariant> >	variant_array_t;



	//
	// Fixed Size Variant
	//

	template <usize Size>
	struct TVariantFS
	{
	// types
	public:
		typedef TVariantFS< Size >	Self;

		enum { SIZE = Size };


	// variables
	private:
		void (*	_dtor) (void *);

		TypeId_t	_id;
		uint8		_data[ SIZE ];


	// methods
	public:
		TVariantFS () : _id(), _dtor(null)
		{
			ZeroMem( _data );
		}


		template <typename T>
		TVariantFS (const T& value) : _id(), _dtor(null)
		{
			ZeroMem( _data );
			Create< const T& >( value );
		}


		template <typename T>
		void Create (const T &value)
		{
			typedef typename TypeTraits::RemoveQualifiers< T >::type	inner_t;
			
			Destroy();

			STATIC_ASSERT( sizeof(inner_t) <= Size, "incorrect size of type" );

			placement_new< inner_t >( _data, value );
			_dtor = &_Destructor< inner_t >;
			_id   = TypeId< inner_t >();
		}


		template <typename InputType, typename RequredType>
		void Create (InputType value)
		{
			typedef typename TypeTraits::RemoveQualifiers< RequredType >::type	inner_t;
			
			Destroy();

			STATIC_ASSERT( sizeof(inner_t) <= Size, "incorrect size of type" );

			placement_new< inner_t >( _data, value );
			_dtor = &_Destructor< inner_t >;
			_id   = TypeId< inner_t >();
		}
		
		/*
		void Move (Self &other)
		{
			Destroy();

			_id		= other._id;
			_dtor	= other._dtor;
			MemCopy( _data, other._data, sizeof(_data) );

			other._dtor = null;
			other.Destroy();
		}
		*/

		void Destroy ()
		{
			if ( _dtor != null )
			{
				_dtor( _data );
				_dtor = null;
				ZeroMem( _data );
			}
			_id = TypeId_t();
		}


		template <typename T>
		Self & operator = (const T& value)
		{
			Create( value );
			return *this;
		}

	
		template <typename T>
		bool IsType () const
		{
			return TypeId<T>() == _id;
		}


		template <typename T>
		const T & Get () const
		{
			if ( IsType<T>() ) {
				return reinterpret_cast< const T& >( _data );
			}
			WARNING( "incorrect requirement type" );
			return *(T *) null;
		}


		template <typename T>
		T & Get ()
		{
			if ( IsType<T>() ) {
				return reinterpret_cast< T& >( _data );
			}
			WARNING( "incorrect requirement type" );
			return *(T *) null;
		}


		void * GetPtr ()
		{
			return IsDefined() ? ((void *) _data) : null;
		}


		void const * GetPtr () const
		{
			return IsDefined() ? ((const void *) _data) : null;
		}


		template <typename T>
		void CopyTo (T &value) const
		{
			value = Get<T>();
		}


		TypeId_t GetValueTypeId () const
		{
			return _id;
		}


		bool IsDefined () const
		{
			return _id != TypeId_t();
		}


	private:
		TVariantFS (const Self &) {}

		RVALUE_REFERENCE_ONLY( TVariantFS (Self &&) {} )

		void operator = (const Self &) {}
		

		template <typename T>
		static void _Destructor (void *ptr)
		{
			((T *) ptr)->~T();
		}
	};


//-------------------------------------------------------------------

}	// UXTypesExt
}	// UX_STL

#endif	// _UXS_VARIANT_H